/**
  ******************************************************************************
  * @file    modbus_common.c
  * @author  ashuai0110
  * @version V2.3
  * @date    2024-07-01
  * @brief   modbus通用文件
  *
  ******************************************************************************
  * @attention
  *
  * 版权声明:内容为编者(ashuai0110)原创,使用请注明出处,当然,你也可以不这样做^_^
  * 出处链接:https://gitee.com/ashuai0110/mcu_reuse_development_module.git
  *
  * Change Logs:
  * Date          Author        Notes
  * 2023-10-27    ashuai0110    完成基本内容
  * 2024-01-08    ashuai0110    修改查表的crc16为计算的
  *
  ******************************************************************************
  */

/* 包含头文件-----------------------------------------------------------------*/
#include "modbus_common.h"

/**
 * @addtogroup modbus
 * @{
 */

/**
 * @defgroup modbus_common modbus_common
 * @{
 */

/* 私有宏定义-----------------------------------------------------------------*/

/* 私有类型定义---------------------------------------------------------------*/

/* 私有变量-------------------------------------------------------------------*/
/**
 * @defgroup modbus_common_local_variables modbus common local variables
 * @{
 */

/* HEX转ASCII表 */
static const char HEX_TO_ASCII[] = {0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0X41,0X42,0X43,0X44,0X45,0X46};

/**
 * @}
 */

/* 全局变量-------------------------------------------------------------------*/

/* 私有函数原型---------------------------------------------------------------*/

/**
 * @defgroup modbus_common_global_functions modbus common global functions
 * @{
 */

/**
  * @brief  CRC16-MODBUS校验
  *
  * @param  data : 校验数据
  *
  * @param  len  : 校验数据长度
  *
  * @retval 校验结果
  */
uint16_t mb_check_crc16(uint8_t *data, uint16_t len)
{
    uint8_t cnt;
    uint16_t crc = 0xFFFF;

    while(len--)
    {
        crc ^= *data;
        data++;
        for(cnt = 0; cnt < 8; ++cnt)
        {
            if(crc & 0x01)
            {
                crc = (crc >> 1) ^ 0xA001;
            }
            else
            {
                crc >>= 1;
            }
        }
    }

    return crc;
}

/**
  * @brief  LRC校验
  *
  * @param  data : 校验数据
  *
  * @param  len  : 校验数据长度
  *
  * @retval 校验结果
  */
uint8_t mb_check_lrc(uint8_t *data, uint16_t len)
{
    uint8_t lrc = 0;
    
    for(uint16_t index = 0; index < len; index++)
    {
        lrc += data[index];
    }
    
    lrc = (~lrc) + 1;
    
    return lrc;
}

/**
  * @brief  HEX转ASCII
  *
  * @param  pHex   : hex数据缓存区
  *
  * @param  num    : hex数据数量
  *
  * @param  pAscii : ascii数据存储区
  *
  * @retval None
  */
void mb_hex_to_ascii(uint8_t *pHex, uint16_t num, uint8_t *pAscii)
{
    /* 从后往前转换 */
    for(int16_t index = num - 1; index >= 0; index--)
    {
        pAscii[(index << 1) + 1] = HEX_TO_ASCII[pHex[index] >> 4];
        pAscii[(index << 1) + 2] = HEX_TO_ASCII[pHex[index] & 0xF];
    }
}

/**
  * @brief  ASCII转HEX
  *
  * @param  pAscii : ascii数据缓存区
  *
  * @param  num    : ascii数据数量
  *
  * @param  pHex   : hex数据存储区
  *
  * @retval None
  */
void mb_ascii_to_hex(uint8_t *pAscii, uint16_t num, uint8_t *pHex)
{
    uint8_t index1, index2;
    /* 从前往后转换 */
    for(uint8_t i = 0; i < (num >> 1); i++)
    {
        index1 = i << 1;
        index2 = index1 + 1;
        pHex[i] = ((pAscii[index1] > '9' ? (pAscii[index1] + 0x9) : pAscii[index1]) << 4) | ((pAscii[index2] > '9' ? (pAscii[index2] + 0x9) : pAscii[index2]) & 0xF);
    }
}

/**
  * @brief  设置对应位(一般用于线圈和离散量)
  *
  * @param  srcBuf    : 源数据地址
  *
  * @param  bitOffset : 位偏移量
  *
  * @param  bitNum    : 位数量
  *
  * @param  bitVal    : 位数据
  *
  * @retval None
  */
void mb_set_bits(uint8_t *srcBuf, uint16_t bitOffset, uint8_t bitNum, uint8_t bitVal)
{
    uint8_t byteOffset;
    uint8_t preBitNum;
    uint16_t bitMask;
    uint16_t bitValTmp;
    
    byteOffset = bitOffset / 8; /* 数据索引 */
    preBitNum = bitOffset % 8; /* bit开始的位数 */
    bitMask = ((1 << bitNum) - 1) << preBitNum; /* 掩码位 */
    bitValTmp = bitVal << preBitNum;  /* 数据偏移后 */
    
    *(uint16_t *)&srcBuf[byteOffset] = (*(uint16_t *)&srcBuf[byteOffset] & (~bitMask)) | bitValTmp; /* 修改对应位 */
}

/**
  * @brief  获取对应位(一般用于线圈和离散量)
  *
  * @param  srcBuf    : 源数据地址
  *
  * @param  bitOffset : 位偏移量
  *
  * @param  bitNum    : 位数量
  *
  * @retval 返回对应位
  */
uint8_t mb_get_bits(uint8_t *srcBuf, uint16_t bitOffset, uint8_t bitNum)
{
    uint8_t byteOffset;
    uint8_t preBitNum;
    uint8_t bitMask;
    uint8_t retVal;
    
    byteOffset = bitOffset / 8; /* 数据索引 */
    preBitNum = bitOffset % 8; /* bit开始的位数 */
    bitMask = (1 << bitNum) - 1; /* 掩码位 */
    
    retVal = *(uint16_t *)&srcBuf[byteOffset] >> preBitNum; /* 获取对应位 */
    retVal = retVal & bitMask; /* 保留有效位 */
    
    return retVal;
}

/**
 * @}
 */

/**
 * @}
 */

/**
 * @}
 */

